//------------------------------------------------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include "uart.h"
#include "BuffFifo.h"

#define DISABLE_SEND_ISR 	UCSRB &= ~_BV(UDRIE);	// Disable USART_UDRE interrupts.
#define ENABLE_SEND_ISR 	UCSRB |=  _BV(UDRIE);	// Enable USART_UDRE interrupts.

unsigned char UARTTxFifo[TX_BUFSZ + sizeof(UARTFifo) - 1];
const pUARTFifo TxBuf = (pUARTFifo)UARTTxFifo;

unsigned char UARTRxFifo[RX_BUFSZ + sizeof(UARTFifo) - 1];
const pUARTFifo RxBuf = (pUARTFifo)UARTRxFifo;

void InitUARTBuffer( void )
{
	TxBuf->in = 0;
	TxBuf->out = 0;
	TxBuf->size = TX_BUFSZ;

	RxBuf->in = 0;
	RxBuf->out = 0;
	RxBuf->size = RX_BUFSZ;	
}

int UartPutFifo(pUARTFifo p, char c)
{
	unsigned char t = p->in+1;
	if (t >= p->size)
		t = 0;
	if (t == p->out)	// isFull()
		return FIFO_ERR;
	
	p->buf[p->in] = c;
	p->in = t;	
	return FIFO_OK;
}

unsigned int UartPullFifo(pUARTFifo p)
{
	if (p->in == p->out)	// isEmpty()
		return FIFO_ERR;
		
	unsigned int  c = p->buf[p->out];
	unsigned char t = p->out+1;
	if (t >= p->size)
		t = 0;
	p->out = t;
	return c;
}


int isBufferNotEmpty( pUARTFifo p )
{
	if (p->in == p->out)
		return FALSE;
	else
		return TRUE;
}


void InitUART(uint16_t ubrr)
{

	UBRRL = ubrr;
	UBRRH = (uint8_t)(ubrr>>8);
	UCSRA = (1<<U2X);
	UCSRB = ((1<<TXEN) | (1<<RXEN) | (1<<RXCIE));
	UCSRC = ((1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0));

	InitUARTBuffer();
	
	stdout = stdin = &serial;
}

int isTxNotEmpty( void )
{
	return isBufferNotEmpty(TxBuf);
}

int isRxNotEmpty( void )
{
	return isBufferNotEmpty(RxBuf);
}


int put_c(char c, FILE *stream)	// Non blocking output
{
	int retc;
	retc = UartPutFifo(TxBuf, c);
	ENABLE_SEND_ISR;
	return retc;
}

int put_char(char c, FILE *stream)	// Blocking output
{
	UartPutFifo(TxBuf, c);
	ENABLE_SEND_ISR;	
	return 0;
}

int get_c(FILE *stream)	// Non blocking, return status outside of char range
{
	int retc = UartPullFifo(RxBuf);
	return retc;
}

int get_char(FILE *stream)	// Blocks waiting for something
{
	return UartPullFifo(RxBuf);
}

FILE serial = FDEV_SETUP_STREAM(put_char,
								get_c,
								_FDEV_SETUP_RW);

// We don't care if the buffer is full.  Just signal we got one.
// The task may attempt one extra time to get data out of an empyt
// buffer (getc(), but so what.  Eventually it will block waiting
// for another character to be received.


// UART Receive Complete Interrupt Handler
ISR( USART_RXC_vect )
{
    DISABLE_SEND_ISR;	// Disable Rx interrupt
    //sei();					// Allow other interrupt activity to occur
	UartPutFifo(RxBuf, UDR);// This resets the Rx Interrupt
	
	ENABLE_SEND_ISR;	// Re-enable.
}


// USART Data Register Empty  Interrupt Handler
ISR( USART_UDRE_vect )
{
    DISABLE_SEND_ISR;			// Disable UDRE interrupt
    //sei();							// Allow other stuff to happen
	int c = UartPullFifo(TxBuf);	// Return -1 if empty (enables interrupts)
	if (c >= 0)						// Tricky tight code: only checking sign
	{
		UDR = c;
		ENABLE_SEND_ISR;
	}
}


// UART Transmit Complete Interrupt Handler
ISR( USART_TXC_vect )
{
    DISABLE_SEND_ISR;	

	ENABLE_SEND_ISR;
}
